package com.ttg.common.sftp;

import com.jcraft.jsch.*;
import com.ttg.common.enums.ErrorCodeEnums;
import com.ttg.common.exception.BaseException;
import com.ttg.common.utils.CommUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;

@Slf4j
public class SftpExecutor {

    public static void execute(SftpConfig conf) throws Exception {

        final String serverIp = conf.getServerIp();
        final int port = conf.getPort();
        final String username = conf.getUsername();
        final String password = conf.getPassword();

        final String remotePath = conf.getRemotePath();
        final String remoteFileName = conf.getRemoteFilename();
        final String localPath = conf.getLocalPath();
        final String localFileName = conf.getLocalFilename();

        ChannelSftp sftp = null;
        Session sshSession = null;
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            JSch jsch = new JSch();

            //添加私钥(信任登录方式)
            final String privateKey = conf.getPrivateKey();
            if (StringUtils.isNotBlank(privateKey)) {
                jsch.addIdentity(privateKey);
            }

            sshSession = jsch.getSession(username, serverIp, port);
            log.trace("Session created.");

            //代理配置
            ProxyTypeEnum proxyType = conf.getProxyType();
            String proxyHost = conf.getProxyHost();
            Integer proxyPort = conf.getProxyPort();
            if (StringUtils.isNotBlank(proxyHost) && proxyPort != null) {
                switch (proxyType) {
                    case HTTP:
                        sshSession.setProxy(new ProxyHTTP(proxyHost, proxyPort));
                        break;
                    case SOCKS4:
                        sshSession.setProxy(new ProxySOCKS4(proxyHost, proxyPort));
                        break;
                    case SOCKS5:
                        sshSession.setProxy(new ProxySOCKS5(proxyHost, proxyPort));
                        break;
                    default:
                        //默认不走代理
                        break;
                }
            }

            if (StringUtils.isNotBlank(password)){
                sshSession.setPassword(password);
            }

            Properties sshConfig = new Properties();
            sshConfig.put("StrictHostKeyChecking", "no");
            sshSession.setConfig(sshConfig);
            sshSession.connect();
            log.trace("Session connected.");

            log.trace("Opening Channel begins.");
            Channel channel = sshSession.openChannel("sftp");
            channel.connect();
            sftp = (ChannelSftp) channel;
            log.trace("Connected to " + serverIp + ".");

            final SftpExecTypeEnum tranType = conf.getExecType();
            if (tranType == SftpExecTypeEnum.UPLOAD) {
                mkRecursionDir(sftp, remotePath);
                sftp.cd(remotePath);
                File file = new File(CommUtils.filePathFilterClean(localPath + "/" + localFileName));
                fis = new FileInputStream(file) ;
                sftp.put(fis, remoteFileName);
            } else if (tranType == SftpExecTypeEnum.DELETE) {
                sftp.cd(remotePath);
                try {
                    sftp.rm(remoteFileName);
                } catch (SftpException e) {
                    log.error("文件删除:"+remotePath+remoteFileName+","+e.getMessage());
                }
            } else if (tranType == SftpExecTypeEnum.DOWNLOAD) {
                sftp.cd(remotePath);
                File file = new File(CommUtils.filePathFilterClean(localPath) );
                if (!file.exists()){
                    if (!file.mkdirs()) {
                        log.error(localPath + localFileName + "路径创建失败");
                        throw new BaseException(ErrorCodeEnums.BANK_FILE_PATH_NOT_EXISTS);
                    }

                }
                file = new File(localPath + "/" + localFileName);
                fos = new FileOutputStream(file);
                sftp.get(remoteFileName, fos);
            }
            log.trace("sftp transmit success.");
        } catch (JSchException jsche) {
            log.error(jsche.getMessage(), jsche);
            throw new RuntimeException("sftp connect error: " + serverIp);
        } catch(SftpException sftpe) {
            log.error("sftp file transmit error: " + remoteFileName, sftpe);
            throw new RuntimeException("sftp file transmit error: " + remoteFileName);
        } catch(IOException ioe) {
            log.error(remoteFileName, ioe);
            throw new RuntimeException("sftp file io error: " + remoteFileName);
        } finally {
            if (sftp != null) {
                sftp.disconnect();
            }
            if (sshSession != null) {
                sshSession.disconnect();
            }
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException ioe) {
                    log.error("fis流关闭异常！", ioe);
                }
            }
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException ioe) {
                    log.error("fos流关闭异常！", ioe);
                }
            }
        }
    }

    private static void mkRecursionDir(ChannelSftp sftp, String path) throws SftpException {
        if (path.startsWith("/")) {
            sftp.cd("/");
        }
        String[] folders = path.split("/");
        for (String folder:folders) {
            if (folder.length() > 0) {
                try {
                    sftp.cd(folder);
                } catch (SftpException e) {
                    sftp.mkdir(folder);
                    sftp.cd(folder);
                }
            }
        }
    }
}
